package cn.jbolt.util;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.jboltai.util.other.ParamType;
import org.apache.poi.ss.formula.functions.T;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

public class ObjectUtil {

    /**
     * 将字符串根据类型转成对应的对象
     * @param valueStr
     * @param type
     * @return
     * @param <T>
     */
    public static <T> T convert(String valueStr, Type type) {
        try {
            if (type instanceof ParameterizedType) {
                return (T)convertParameterizedType(valueStr, (ParameterizedType) type);
            } else {
                // 简单类型直接转换
                return (T)convertBasicValue(valueStr, (Class<?>) type);
            }
        } catch (Exception e) {
            throw new RuntimeException("无法将字符串： " + valueStr + " 转成类型" + type, e);
        }
    }

    /**
     * 转换基本数据类型
     * @param str
     * @param clazz
     * @return
     */
    private static <T> T  convertBasicValue(String str, Class<?> clazz) {
        if (clazz == String.class) {
            return (T) str;
        }

        if (StrUtil.isBlank(str)) {
            return (T) ParamType.getDefaultValue(clazz);
        }

        if (clazz == int.class || clazz == Integer.class) {
            return (T) Integer.valueOf(Double.valueOf(str).intValue());
        }
        if (clazz == double.class || clazz == Double.class) {
            return (T) Double.valueOf(str);
        }
        if (clazz == boolean.class || clazz == Boolean.class) {
            if (str.equals("true".intern()) || str.equals("on".intern()) || str.equals("1".intern())) {
                return (T) Boolean.TRUE;
            }
            return (T) Boolean.FALSE;
        }
        if (clazz == long.class || clazz == Long.class) {
            return (T) Long.valueOf(new BigDecimal(str).longValue());
        }
        if (clazz == Date.class ) {
            return (T) DateUtil.parseDate(str);
        }
        if (clazz == java.sql.Date.class) {
            Date date = DateUtil.parseDate(str);
            return date == null ? null : (T) new java.sql.Date(date.getTime());
        }

        if (clazz == Time.class) {
            Date date = DateUtil.parseDate(str);
            return date == null ? null : (T) new Time(date.getTime());
        }
        if (clazz == short.class || clazz == Short.class) {
            return (T) Short.valueOf(new BigDecimal(str).shortValue());
        }
        if (clazz == byte.class || clazz == Byte.class) {
            return (T) Byte.valueOf(new BigDecimal(str).byteValue());
        }
        if (clazz == float.class || clazz == Float.class) {
            return (T) Float.valueOf(new BigDecimal(str).floatValue());
        }
        if (clazz == char.class || clazz == Character.class) {
            return (T) new Character(str.charAt(0));
        }

        if (clazz.isEnum()) {
            try {
                Method getValueOf = clazz.getDeclaredMethod("valueOf", String.class);
                return (T) getValueOf.invoke(clazz, str);
            } catch (Exception e) {
                return null;
            }
        }


        if (clazz == LocalDate.class) {
            return (T) DateUtil.toLocalDate(DateUtil.parseDate(str));
        }
        if (clazz == LocalTime.class) {
            return (T) DateUtil.toLocalTime(DateUtil.parseDate(str));
        }

        if (clazz == LocalDateTime.class) {
            return (T) DateUtil.toLocalDateTime(DateUtil.parseDate(str));
        }

        if (clazz == BigInteger.class) {
            return (T) new BigInteger(str);
        }

        if (clazz == BigDecimal.class) {
            return (T) new BigDecimal(str);
        }
        try {
            return (T) JSONUtil.MAPPER.readValue(str, clazz);
        } catch (JsonProcessingException e) {
            throw new ClassCastException(str + "不支持直接转化为" + clazz.getSimpleName() + "类型数据");
        }

    }


    private static final ConcurrentHashMap<TypeCacheKey, JavaType> TYPE_CACHE = new ConcurrentHashMap<>();
    /**
     * 处理泛型类型的转换
     */
    private static Object convertParameterizedType(String jsonString, ParameterizedType type) throws Exception {
        // 获取原始类型
        Class<?> rawType = (Class<?>) type.getRawType();
        // 获取泛型参数
        Type[] actualTypeArguments = type.getActualTypeArguments();

        // 创建 JavaType
        JavaType javaType = getJavaType(rawType, actualTypeArguments);

        return JSONUtil.MAPPER.readValue(jsonString, javaType);
    }
    /**
     * 获取或创建JavaType，使用缓存
     */
    private static JavaType getJavaType(Class<?> rawType, Type[] actualTypeArguments) {
        TypeCacheKey cacheKey = new TypeCacheKey(rawType, actualTypeArguments);

        return TYPE_CACHE.computeIfAbsent(cacheKey, key -> createJavaType(rawType, actualTypeArguments));
    }
    /**
     * 递归创建JavaType
     */
    private static JavaType createJavaType(Class<?> rawType, Type[] actualTypeArguments) {
        JavaType[] javaTypes = new JavaType[actualTypeArguments.length];

        for (int i = 0; i < actualTypeArguments.length; i++) {
            Type actualType = actualTypeArguments[i];
            if (actualType instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) actualType;
                Class<?> actualRawType = (Class<?>) parameterizedType.getRawType();
                javaTypes[i] = createJavaType(actualRawType, parameterizedType.getActualTypeArguments());
            } else {
                javaTypes[i] = JSONUtil.MAPPER.getTypeFactory().constructType(actualType);
            }
        }

        return JSONUtil.MAPPER.getTypeFactory().constructParametricType(rawType, javaTypes);
    }

    /**
     * 类型缓存的Key
     */
    private static class TypeCacheKey {
        private final Class<?> rawType;
        private final Type[] actualTypeArguments;

        public TypeCacheKey(Class<?> rawType, Type[] actualTypeArguments) {
            this.rawType = rawType;
            this.actualTypeArguments = actualTypeArguments;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            TypeCacheKey that = (TypeCacheKey) o;
            return Objects.equals(rawType, that.rawType) &&
                    Arrays.equals(actualTypeArguments, that.actualTypeArguments);
        }

        @Override
        public int hashCode() {
            int result = Objects.hash(rawType);
            result = 31 * result + Arrays.hashCode(actualTypeArguments);
            return result;
        }
    }

    /**
     * 判断一个值是否为常见类型，包括:
     * 1. 基本数据类型及其包装类
     * 2. 日期时间相关类型
     * 3. 字符串相关类型
     * 4. 数值类型(BigDecimal, BigInteger)
     *
     * @param value 待检查的值
     * @return 如果是常见类型返回true，否则返回false
     */
    public static boolean isCommonTypeValue(Object value) {
        if (value == null) {
            return true;
        }

        Class<?> clazz = value.getClass();
        // 基本数据类型
        if (clazz.isPrimitive()) {
            return true;
        }
        // 基本数据类型的包装类
        if (isPrimitiveWrapper(clazz)) {
            return true;
        }



        // 字符串相关类型
        if (CharSequence.class.isAssignableFrom(clazz)) {
            return true;
        }

        // 日期时间相关类型
        if (isDateTimeType(clazz)) {
            return true;
        }

        // 数值类型
        if (isNumberType(clazz)) {
            return true;
        }

        return false;
    }

    /**
     * 判断是否为基本数据类型的包装类
     */
    private static boolean isPrimitiveWrapper(Class<?> clazz) {
        return clazz == Boolean.class ||
                clazz == Character.class ||
                clazz == Byte.class ||
                clazz == Short.class ||
                clazz == Integer.class ||
                clazz == Long.class ||
                clazz == Float.class ||
                clazz == Double.class ||
                clazz == Void.class;
    }


    /**
     * 判断是否为日期时间相关类型
     */
    private static boolean isDateTimeType(Class<?> clazz) {
        return clazz == Date.class ||
                clazz == Calendar.class ||
                clazz == Timestamp.class ||
                clazz == LocalDate.class ||
                clazz == LocalTime.class ||
                clazz == LocalDateTime.class;
    }

    /**
     * 判断是否为数值类型
     */
    private static boolean isNumberType(Class<?> clazz) {
        return clazz == BigDecimal.class ||
                clazz == BigInteger.class ||
                Number.class.isAssignableFrom(clazz);
    }


}
